package org.openkanban.client.ui.board;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import org.openkanban.client.cometd.CometClient;
import org.openkanban.client.cometd.SubscribeCallback;
import org.openkanban.client.login.LogIn;
import org.openkanban.client.ui.presenter.GetBoardByIdRequestCallback;
import org.openkanban.client.ui.presenter.GetDefaultBoardByUserIdRequestCallback;
import org.openkanban.client.ui.presenter.GetNewActivityByIdRequestCallback;
import org.openkanban.client.ui.presenter.GetNewKanbanByIdRequestCallback;
import org.openkanban.client.ui.presenter.PreparedRequest;
import org.openkanban.client.util.RootPanel;
import org.openkanban.shared.dto.ActivityDto;
import org.openkanban.shared.dto.BoardDto;
import org.openkanban.shared.dto.KanbanDto;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.json.client.JSONObject;
import com.smartgwt.client.types.Overflow;
import com.smartgwt.client.widgets.Canvas;
import com.smartgwt.client.widgets.layout.HLayout;
import com.smartgwt.client.widgets.layout.VLayout;

/**
 * GUI Element for the WhiteBoardView. Part of the Board Composite. Should react
 * if board switches.
 */
public class WhiteBoardView extends VLayout implements BoardComposite {

    private List<ActivityView> activityViewList;
    private HLayout activitiesHLayout;
    private BoardDto boardDto;
    private EnterBoardNameView enterBoardNameView;

    private CometClient cometClient;

    public WhiteBoardView() {
	cometClient = RootPanel.main.getCometClient();

	activityViewList = new ArrayList<ActivityView>();

	activitiesHLayout = new HLayout();
	activitiesHLayout.setHeight100();

	setShowEdges(true);
	setMargin(5);
	setEdgeImage("corners/wb3.png");
	setEdgeSize(23);
	setPadding(5);
	setHeight100();
	setBackgroundColor("#eeeeee");
	setOverflow(Overflow.AUTO);
	setVisible(true);

	addMember(activitiesHLayout);
	RootPanel.boardComponent.addComposite(this);

	loadDefaultBoard();
    }

    public void loadDefaultBoard() {
	PreparedRequest prepReq = new PreparedRequest("board", "user/" + LogIn.getUserDto().getUserId().toString());
	prepReq.sendAsyncRequest(new GetDefaultBoardByUserIdRequestCallback());
    }

    @Override
    public void switchBoard(Long boardId) {
	removeActivitiesHLayoutMembers();
	activityViewList.clear();
	PreparedRequest prepRequest = new PreparedRequest("board", boardId.toString());
	prepRequest.sendAsyncRequest(new GetBoardByIdRequestCallback());
    }

    public void addActivity(ActivityView activityView) {
	if (activityView.getActivityDto() == null || !activityExists(activityView.getActivityDto().getActivityId())) {
	    activitiesHLayout.addMember(activityView);
	    activityViewList.add(activityView);
	    if (activityViewList.size() == 1) {
		RootPanel.main.getBoard().showNewKanbanButton();
	    }
	}
    }

    public void removeActivity(ActivityView activityView) {
	activitiesHLayout.removeMember(activityView);
	activityViewList.remove(activityView);
    }

    public void removeActivitiesHLayoutMembers() {
	Canvas[] members = activitiesHLayout.getMembers();
	activitiesHLayout.removeMembers(members);
	activityViewList.clear();
    }

    public boolean hasActivity() {
	return activityViewList.size() > 0;
    }

    public ActivityDto getFirstActicityDto() {
	Long min = Long.MAX_VALUE;
	ActivityDto minActvity = new ActivityDto();
	for (ActivityView activityView : activityViewList) {
	    ActivityDto activityDto = activityView.getActivityDto();
	    if (activityDto != null) {
		if (activityDto.getActivityId() < min) {
		    min = activityDto.getActivityId();
		    minActvity = activityDto;
		}
	    }
	}
	return minActvity;
    }

    public void subscribe() {
	subscribeNewKanban();
	subscribeKanbanMove();
	subscribeNewActivity();
	subscribeUpdateActivity();
	subscribeRemoveActivity();
    }

    private void subscribeUpdateActivity() {
	cometClient.subscribe("/board/" + boardDto.getBoardId().toString() + "/updateactivity", new SubscribeCallback() {
	    @Override
	    public void execute(JavaScriptObject jso) {
		JSONObject jsonObject = new JSONObject(jso);
		JSONObject jsonDataObject = jsonObject.get("data").isObject();

		Long activityId = Long.parseLong(jsonDataObject.get("activityId").isString().stringValue());

		for (ActivityView activityView : activityViewList) {
		    if (activityView.getActivityDto().getActivityId() == activityId) {
			activityView.updateActivityDto();
			break;
		    }
		}
	    }
	});
    }

    private void subscribeNewActivity() {
	cometClient.subscribe("/board/" + boardDto.getBoardId().toString() + "/newactivity", new SubscribeCallback() {
	    @Override
	    public void execute(JavaScriptObject jso) {
		JSONObject jsonObject = new JSONObject(jso);
		JSONObject jsonDataObject = jsonObject.get("data").isObject();

		Long activityId = Long.parseLong(jsonDataObject.get("activityId").isString().stringValue());

		if (!activityExists(activityId)) {
		    PreparedRequest prepReq = new PreparedRequest("activity", activityId.toString());
		    prepReq.sendAsyncRequest(new GetNewActivityByIdRequestCallback());
		}
	    }
	});
    }

    private void subscribeRemoveActivity() {
	cometClient.subscribe("/board/" + boardDto.getBoardId().toString() + "/removeactivity", new SubscribeCallback() {
	    @Override
	    public void execute(JavaScriptObject jso) {
		JSONObject jsonObject = new JSONObject(jso);
		JSONObject jsonDataObject = jsonObject.get("data").isObject();

		Long activityId = Long.parseLong(jsonDataObject.get("activityId").isString().stringValue());

		if (activityExists(activityId)) {
		    ActivityView activityViewToRemove = getActivityViewById(activityId);
		    removeActivity(activityViewToRemove);
		}
	    }
	});
    }

    private void subscribeNewKanban() {
	cometClient.subscribe("/board/" + getBoardDto().getBoardId().toString() + "/newkanban", new SubscribeCallback() {
	    @Override
	    public void execute(JavaScriptObject jso) {
		JSONObject jsonObject = new JSONObject(jso);
		JSONObject jsonDataObject = jsonObject.get("data").isObject();

		Long kanbanId = Long.parseLong(jsonDataObject.get("kanbanId").isString().stringValue());

		if (!getActivityViewList().get(0).kanbanViewExists(kanbanId)) {
		    PreparedRequest prepReq = new PreparedRequest("kanban", kanbanId.toString());
		    prepReq.sendAsyncRequest(new GetNewKanbanByIdRequestCallback());
		}
	    }
	});
    }

    private void subscribeKanbanMove() {
	cometClient.subscribe("/board/" + boardDto.getBoardId().toString() + "/kanbanmove", new SubscribeCallback() {
	    @Override
	    public void execute(JavaScriptObject jso) {
		JSONObject jsonObject = new JSONObject(jso);
		JSONObject jsonDataObject = jsonObject.get("data").isObject();

		Long kanbanId = Long.parseLong(jsonDataObject.get("kanbanId").isString().stringValue());
		Long sourceActivityId = Long.parseLong(jsonDataObject.get("sourceActivityId").isString().stringValue());
		Long targetActivityId = Long.parseLong(jsonDataObject.get("targetActivityId").isString().stringValue());

		ActivityView sourceActivityView = getActivityViewById(sourceActivityId);
		ActivityView targetActivityView = getActivityViewById(targetActivityId);

		if (sourceActivityView.kanbanViewExists(kanbanId)) {
		    KanbanDto kanbanDto = sourceActivityView.getKanbanViewMap().get(kanbanId).getKanbanDto();
		    kanbanDto.setPullable(false);
		    targetActivityView.addKanban(kanbanDto);
		    sourceActivityView.removeKanbanView(kanbanId);
		}
	    }
	});
    }

    public void publishNewActivity(JavaScriptObject jso) {
	cometClient.publish("/board/" + boardDto.getBoardId().toString() + "/newactivity", jso);
    }

    public void publishRemoveActivity(JavaScriptObject jso) {
	cometClient.publish("/board/" + boardDto.getBoardId().toString() + "/removeactivity", jso);
    }

    public void publishUpdateActivity(JavaScriptObject jso) {
	cometClient.publish("/board/" + boardDto.getBoardId().toString() + "/updateactivity", jso);
    }

    public void publishNewKanban(JavaScriptObject jso) {
	cometClient.publish("/board/" + boardDto.getBoardId().toString() + "/newkanban", jso);
    }

    public void publishKanbanMove(JavaScriptObject jso) {
	cometClient.publish("/board/" + boardDto.getBoardId().toString() + "/kanbanmove", jso);
    }

    private boolean activityExists(Long activityId) {
	for (ActivityView activityView : activityViewList) {
	    if (activityView.getActivityDto().getActivityId() == activityId) {
		return true;
	    }
	}
	return false;
    }

    public void showEnterBoardNameView() {
	enterBoardNameView = new EnterBoardNameView();
	hideAllActivities();
	activitiesHLayout.addMember(enterBoardNameView);
    }

    public void hideEnterBoardNameView() {
	if (enterBoardNameView != null) {
	    activitiesHLayout.removeMember(enterBoardNameView);
	}
	showAllActivites();
	if (activityViewList.size() > 0) {
	    RootPanel.main.getBoard().showNewKanbanButton();
	}
    }

    private void showAllActivites() {
	for (ActivityView activityView : activityViewList) {
	    activityView.show();
	}
    }

    private void hideAllActivities() {
	for (ActivityView activityView : activityViewList) {
	    activityView.hide();
	}
    }

    public BoardDto getBoardDto() {
	if (boardDto == null) {
	    boardDto = new BoardDto();
	}
	return boardDto;
    }

    public void setBoardDto(BoardDto boardDto) {
	this.boardDto = boardDto;
	addActivitiesToBoardIfExists(boardDto);
	subscribe();
    }

    private void addActivitiesToBoardIfExists(BoardDto boardDto) {
	if (boardDto.getActivities() != null && !boardDto.getActivities().isEmpty()) {
	    // sort activited ordered by id
	    Map<Long, ActivityDto> sortedActivityMap = new TreeMap<Long, ActivityDto>();
	    for (ActivityDto activityDto : boardDto.getActivities()) {
		sortedActivityMap.put(activityDto.getActivityId(), activityDto);
	    }

	    // add activities to board
	    for (Long activityId : sortedActivityMap.keySet()) {
		ActivityView newActivityView = new ActivityView(sortedActivityMap.get(activityId), boardDto.getBoardId());
		addActivity(newActivityView);
	    }
	}
    }

    public HLayout getActivitiesHLayout() {
	return activitiesHLayout;
    }

    public List<ActivityView> getActivityViewList() {
	return activityViewList;
    }

    public ActivityView getActivityViewById(Long activityId) {
	for (ActivityView activityView : activityViewList) {
	    if (activityView.getActivityDto().getActivityId() == activityId) {
		return activityView;
	    }
	}

	return null;
    }

}
